﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using CoreLib.Communication.Packets;

namespace CoreLib.Communication.Cryptography
{
    public static class DHKeyExchange
    {
        private static Random Rand = new Random();
        public class ServerKeyExchange
        {
            OpenSSL.DH _keyExchange;
            byte[] _serverIv;
            byte[] _clientIv;

            public Packet CreateServerKeyPacket()
            {
                _clientIv = new byte[8];
                _serverIv = new byte[8];
                string P = "E7A69EBDF105F2A6BBDEAD7E798F76A209AD73FB466431E2E7352ED262F8C558F10BEFEA977DE9E21DCEE9B04D245F300ECCBBA03E72630556D011023F9E857F";
                string G = "05";
                _keyExchange = new OpenSSL.DH(OpenSSL.BigNumber.FromHexString(P), OpenSSL.BigNumber.FromHexString(G));
                _keyExchange.GenerateKeys();
                return GeneratePacket(_serverIv, _clientIv, P, G, _keyExchange.PublicKey.ToHexString());
            }
            public Cryptography.GameCryptography HandleClientKeyPacket(string PublicKey, Cryptography.GameCryptography cryptographer)
            {
                byte[] key = _keyExchange.ComputeKey(OpenSSL.BigNumber.FromHexString(PublicKey));
                cryptographer.SetKey(DHKey((key)));
                cryptographer.SetIvs(_clientIv, _serverIv);
                return cryptographer;
            }
            public byte[] DHKey(byte[] key)
            {
                MD5CryptoServiceProvider MD5 = new MD5CryptoServiceProvider();
                string text = HexString(MD5.ComputeHash(key, 0, key.TakeWhile((byte x) => x != 0).Count<byte>()));
                string str = HexString(MD5.ComputeHash(Encoding.UTF7.GetBytes(text + text)));
                string s = text + str;
                return Encoding.UTF7.GetBytes(s);
            }
            private string HexString(byte[] bytes)
            {
                char[] array = new char[bytes.Length * 2];
                int i = 0;
                int num = 0;
                while (i < bytes.Length)
                {
                    byte num2 = (byte)(bytes[i] >> 4);
                    array[num] = (char)((num2 > 9) ? (num2 + 55 + 32) : (num2 + 48));
                    num2 = (byte)(bytes[i] & 15);
                    array[++num] = (char)((num2 > 9) ? (num2 + 55 + 32) : (num2 + 48));
                    i++;
                    num++;
                }
                return new string(array);
            }
            public Packet GeneratePacket(byte[] ServerIV1, byte[] ServerIV2, string P, string G, string ServerPublicKey)
            {
                int PAD_LEN = 11;
                int _junk_len = 12;
                string tqs = "TQServer";
                MemoryStream ms = new MemoryStream();
                byte[] pad = new byte[PAD_LEN];
                Rand.NextBytes(pad);
                byte[] junk = new byte[_junk_len];
                Rand.NextBytes(junk);
                int size = 75 + P.Length + G.Length + ServerPublicKey.Length;
                BinaryWriter bw = new BinaryWriter(ms);
                bw.Write(pad);
                bw.Write(size - PAD_LEN);
                bw.Write((UInt32)_junk_len);
                bw.Write(junk);
                bw.Write((UInt32)ServerIV2.Length);
                bw.Write(ServerIV2);
                bw.Write((UInt32)ServerIV1.Length);
                bw.Write(ServerIV1);
                bw.Write((UInt32)P.ToCharArray().Length);
                foreach (char fP in P.ToCharArray())
                {
                    bw.BaseStream.WriteByte((byte)fP);
                }
                bw.Write((UInt32)G.ToCharArray().Length);
                foreach (char fG in G.ToCharArray())
                {
                    bw.BaseStream.WriteByte((byte)fG);
                }
                bw.Write((UInt32)ServerPublicKey.ToCharArray().Length);
                foreach (char SPK in ServerPublicKey.ToCharArray())
                {
                    bw.BaseStream.WriteByte((byte)SPK);
                }
                foreach (char tq in tqs.ToCharArray())
                {
                    bw.BaseStream.WriteByte((byte)tq);
                }
                byte[] packet = new byte[ms.Length];
                packet = ms.ToArray();
                ms.Close();
                Packet stream = new Packet(packet.Length);
                stream.WriteByteArray(packet);
                return stream;
            }
        }
    }
}
